﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using alib.Debugging;

namespace agree
{
	public interface IDiscriminant
	{
		String Description { get; }

		IEnumerable<IDependentDiscriminant> GetDependants();
		void AddDependant(IDependentDiscriminant d);
	}

	public interface IDependentDiscriminant : IDiscriminant
	{
		bool DependsOn(IDiscriminant other);
		HashSet<IDiscriminant> GetDependencies();
		void AddDependency(IDiscriminant dependee);
		IEnumerable<IDiscriminant> GetDependencyTemplates();
	}

	public abstract class Discriminant : IEquatable<IDiscriminant>, IDiscriminant
	{
		protected Discriminant(string formatted)
		{
			this._formatted = formatted;
		}

		readonly string _formatted;

		public virtual String Description { get { return this.ToString(); } }

		public override bool Equals(object obj)
		{
			return obj is Discriminant && this.Equals((Discriminant)obj);
		}

		public override string ToString()
		{
			return _formatted.ToString();
		}

		public override int GetHashCode()
		{
			return _formatted.GetHashCode();
		}

		static Dictionary<string, IDiscriminant> disc_lookup = new Dictionary<string, IDiscriminant>();

		public bool Equals(IDiscriminant other)
		{
			return other is Discriminant && _formatted.Equals(((Discriminant)other)._formatted);
		}

		public IEnumerable<IDependentDiscriminant> GetDependants()
		{
			return dependants;
		}

		public void AddDependant(IDependentDiscriminant d)
		{
			dependants.Add(d);
		}

		HashSet<IDependentDiscriminant> dependants = new HashSet<IDependentDiscriminant>();
	};

	public abstract class DependentDiscriminant : Discriminant, IDependentDiscriminant
	{
		public DependentDiscriminant(string formatted)
			: base(formatted)
		{

		}

		public abstract IEnumerable<IDiscriminant> GetDependencyTemplates();

		public bool DependsOn(IDiscriminant other)
		{
			return dependencies.Contains(other);
		}

		public HashSet<IDiscriminant> GetDependencies()
		{
			return dependencies;
		}

		public void AddDependency(IDiscriminant dependee)
		{
			dependencies.Add(dependee);
		}

		private HashSet<IDiscriminant> dependencies = new HashSet<IDiscriminant>();
	}

	public class EPSpanLinkDiscriminant : DependentDiscriminant, IHasSpanDiscriminant
	{
		public EPSpanLinkDiscriminant(TokenSpan ts1, string r, TokenSpan ts2)
			: base(String.Format("EPLink::{0}:{1}:{2}", ts1, r, ts2))
		{
			this.ts = ts1;
			this.role = r;
			this.target_ts = ts2;

		}

		public override string Description
		{
			get
			{
				return String.Format("\"{0}\".{1} = \"{2}\"", ts.Surface, role, target_ts.Surface);
			}
		}

		public override IEnumerable<IDiscriminant> GetDependencyTemplates()
		{
			return new IDiscriminant[] { new ConstituentDiscriminant(ts), new ConstituentDiscriminant(target_ts) };
		}

		public TokenSpan Span
		{
			get { return ts; }
		}

		public string Role { get { return role; } }

		public TokenSpan TargetSpan { get { return target_ts; } }

		TokenSpan ts, target_ts;
		string role;
	}

	public class PredRolePredDiscriminant : DependentDiscriminant, IHasSpanDiscriminant
	{
		public PredRolePredDiscriminant(PredSpan ps1, string r, PredSpan ps2)
			: base(String.Format("PRP::{0}:{1}:{2}", ps1, r, ps2))
		{
			this.ps1 = ps1;
			this.role = r;
			this.ps2 = ps2;
		}

		public override string Description
		{
			get
			{
				if (ps2 == null)
					return String.Format("{0}.{1} = None", ps1, role);

				return String.Format("{0}.{1} = {2}", ps1, role, ps2);
			}
		}

		public override IEnumerable<IDiscriminant> GetDependencyTemplates()
		{
			return new IDiscriminant[] { new PredDiscriminant(PS1) };
		}

		public PredSpan PS1 { get { return ps1; } }
		public string Role { get { return role; } }
		public PredSpan PS2 { get { return ps2; } }

		public TokenSpan Span
		{
			get { return ps1.Span; }
		}

		PredSpan ps1, ps2;
		string role;
	}

	public class PredSpan
	{
		public PredSpan(string pred, TokenSpan span)
		{
			this.pred = pred;
			this.span = span;
		}

		public override String ToString()
		{
			return !Span.Equals(TokenSpan.NoneTokenSpan) ? String.Format("{0}:{1}-{2}", pred, span.Start, span.End) : "None";
		}

		public override bool Equals(object obj)
		{
			PredSpan ps = obj as PredSpan;
			return ps != null && this.Span.Start == ps.Span.Start && this.Span.End == ps.Span.End;
		}

		public override int GetHashCode()
		{
			return Pred.GetHashCode() ^ Span.GetHashCode();
		}

		private string pred;
		private TokenSpan span;

		public String Pred { get { return pred; } }
		public TokenSpan Span { get { return span; } }

		public static PredSpan NonePredSpan = new PredSpan(null, TokenSpan.NoneTokenSpan);
	}

	public class PredDiscriminant : DependentDiscriminant, IHasSpanDiscriminant
	{
		public PredDiscriminant(PredSpan ps)
			: base(String.Format("P::{0}", ps))
		{
			this.ps = ps;
		}

		PredSpan ps;
		public PredSpan PS { get { return ps; } }

		public override string Description
		{
			get
			{
				return String.Format("has pred \"{0}\" for \"{1}\"?", ps.Pred, ps.Span.Surface);
			}
		}

		public TokenSpan Span
		{
			get { return ps.Span; }
		}

		public override IEnumerable<IDiscriminant> GetDependencyTemplates()
		{
			return new IDiscriminant[] { new ConstituentDiscriminant(Span) };
		}

	};

	public class PredPropValDiscriminant : Discriminant, IHasSpanDiscriminant
	{
		public PredPropValDiscriminant(PredSpan ps, string prop, string val)
			: base(String.Format("PPV::{0}:{1}-{2}:{3}:{4}", ps.Pred, ps.Span.Start, ps.Span.End, prop, val))
		{
			this.ps = ps;
			this.prop = prop;
			this.val = val;
		}

		public TokenSpan Span
		{
			get { return ps.Span; }
		}

		public PredSpan PS { get { return ps; } }
		public string Prop { get { return prop; } }
		public string Value { get { return val; } }

		PredSpan ps;
		string prop, val;
	};

	public class RuleSpanDiscriminant : DependentDiscriminant, IHasSpanDiscriminant
	{
		public RuleSpanDiscriminant(TokenSpan span, string rule_name)
			: base(String.Format("RS::{0}:{1}:{2}", span.Start, span.End, rule_name))
		{
			this.span = span;
			this.rule = rule_name;
		}

		public override IEnumerable<IDiscriminant> GetDependencyTemplates()
		{
			return new IDiscriminant[] { new ConstituentDiscriminant(span) };
		}

		public TokenSpan Span { get { return span; } }
		public string Rule { get { return rule; } }

		public override string Description { get { return String.Format("\"{0}\" - {1}", span.Surface, rule); } }

		TokenSpan span;
		string rule;
	};

	public class TokenSpan
	{
		public TokenSpan(int start, int end, string surface)
		{
			this.start = start;
			this.end = end;
			this.surface = surface;
		}

		public override bool Equals(object obj)
		{
			TokenSpan other = obj as TokenSpan;
			return other != null && other.Start == Start && other.End == End;
		}

		public override int GetHashCode()
		{
			return (start * 1000 + end).GetHashCode();
		}

		public override string ToString()
		{
			return String.Format("{0}-{1}", Start, End);
		}

		public int Start { get { return start; } }
		public int End { get { return end; } }
		public string Surface { get { return surface; } }

		int start, end;
		string surface;

		public static TokenSpan NoneTokenSpan = new TokenSpan(-1, -1, "None");
	}

	public interface IHasSpanDiscriminant : IDiscriminant
	{
		TokenSpan Span { get; }
	}


	public class ConstituentDiscriminant : Discriminant, IHasSpanDiscriminant
	{
		public ConstituentDiscriminant(TokenSpan span)
			: base(String.Format("Cons::{0}:{1}", span.Start, span.End))
		{
			this.span = span;
		}

		public override string Description { get { return String.Format("Is \"{0}\" a constituent?", span.Surface); } }

		public TokenSpan Span { get { return span; } }

		TokenSpan span;
	}

	public class ConstituentLabelDiscriminant : DependentDiscriminant, IHasSpanDiscriminant
	{
		public ConstituentLabelDiscriminant(TokenSpan span, String cat)
			: base(String.Format("CLbl::{0}:{1}:{2}", span.Start, span.End, cat))
		{
			this.span = span;
			this.cat = cat;
		}

		public override IEnumerable<IDiscriminant> GetDependencyTemplates()
		{
			return new IDiscriminant[] { new ConstituentDiscriminant(span) };
		}

		static char[] an_chars = "AEFHILMNORSXY".ToCharArray();
		public override String Description { get { return String.Format("Is \"{0}\" a{1} {2}?", span.Surface, Array.IndexOf<Char>(an_chars, cat[0]) != -1 ? "n" : "", cat); } }

		public TokenSpan Span { get { return span; } }
		public String Label { get { return cat; } }

		TokenSpan span;
		string cat;
	};
}
